﻿namespace Pathfinder.Base.Constructs
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using Bridge.Base.Constructs;

    public class Skill : ChangableObject
    {
        /// <summary>
        /// Creates a base skill based on a name.
        /// </summary>
        /// <param name="name">Name of the skill.</param>
        /// <param name="subtype">Subtype name of the skill.</param>
        /// <returns>Skill with base information.</returns>
        /// <exception cref="ArgumentNullException">Thrown if name is null, or subtype is null and the name has subskills.</exception>
        /// <exception cref="ArgumentException">Thrown if name is empty, name is not a skill, or name skill does not have subtype.</exception>
        public static Skill CreateBaseSkill(string name, string subtype)
        {
            CreateBaseSkills();

            if (name == null) throw new ArgumentNullException("name");
            if (name.Length == 0) throw new ArgumentException("name cannot be empty", "name");
            if (!sBaseSkills.ContainsKey(name)) throw new ArgumentException(string.Format("skill {0} does not exist.", name), "name");
            if (subtype == null && sSubskills.ContainsKey(name)) throw new ArgumentNullException("subtype", "If skill has subskills, you must specify the skill to create.");
            if (subtype != null && !sSubskills.ContainsKey(name)) throw new ArgumentException("subtype", string.Format("This subtype {0} does not exist.", subtype));
            if (subtype != null)
            {
                bool found = false;
                foreach (string testSubtype in sSubskills[name])
                {
                    if (testSubtype.Equals(subtype, StringComparison.OrdinalIgnoreCase))
                    {
                        found = true;
                    }
                }
                if (!found) throw new ArgumentException(string.Format("skill {0} does not have subtype {1}", name, subtype), "subtype");
            }

            return new Skill() { Name = name, Subtype = subtype, BaseStatType = sBaseSkills[name], AlternateStatType = sBaseSkills[name] };
        }

        /// <summary>
        /// Gets all of the skill names.
        /// </summary>
        /// <returns>ReadOnlyCollection of skill names.</returns>
        public static ReadOnlyCollection<string> GetAllSkillNames()
        {
            CreateBaseSkills();

            return new ReadOnlyCollection<string>(new List<string>(sBaseSkills.Keys));
        }

        /// <summary>
        /// Checks if a skill name has subskills.
        /// </summary>
        /// <param name="name">Skill name to search for.</param>
        /// <returns>True if the skill has subskills; otherwise, false.</returns>
        /// <exception cref="ArgumentNullException">Thrown if name is null.</exception>
        /// <exception cref="ArgumentException">Thrown if name is empty, or name does is not a valid skill name.</exception>
        public static bool HasSubskills(string name)
        {
            CreateBaseSkills();

            if (name == null) throw new ArgumentNullException("name");
            if (name.Length == 0) throw new ArgumentException("name cannot be empty", "name");
            if (!sBaseSkills.ContainsKey(name)) throw new ArgumentException(string.Format("skill {0} does not exist.", name), "name");

            return sSubskills.ContainsKey(name);
        }

        /// <summary>
        /// Gets all of the subskill names for a certain skill.
        /// </summary>
        /// <param name="name">Subskill name.</param>
        /// <returns>ReadOnlyCollection subskill names.</returns>
        /// <exception cref="ArgumentNullException">Thrown if skill name is null.</exception>
        /// <exception cref="ArgumentException">Thrown if name is empty, or skill does not have subskills.</exception>
        public static ReadOnlyCollection<string> GetAllSubskillNames(string name)
        {
            CreateBaseSkills();

            if (name == null) throw new ArgumentNullException("name");
            if (name.Length == 0) throw new ArgumentException("name cannot be empty", "name");
            if (!sSubskills.ContainsKey(name)) throw new ArgumentException("skill does not have subskills.", "name");

            return sSubskills[name].AsReadOnly();
        }

        /// <summary>
        /// Creates a base skill list for a character.
        /// </summary>
        /// <returns>A skill list with all the base skills.</returns>
        public static List<Skill> CreateSkillList()
        {
            List<Skill> skillList = new List<Skill>();

            foreach (string name in GetAllSkillNames())
            {
                if (!HasSubskills(name))
                {
                    skillList.Add(CreateBaseSkill(name, null));
                }
            }

            return skillList;
        }

        /// <summary>
        /// Gets the name.
        /// </summary>
        public string Name
        {
            get { return mName; }
            private set { CheckPropertyChanged<string>("Name", ref mName, ref value); }
        }

        /// <summary>
        /// Gets the subtype
        /// </summary>
        public string Subtype
        {
            get { return mSubtype; }
            private set { CheckPropertyChanged<string>("Subtype", ref mSubtype, ref value); }
        }

        /// <summary>
        /// Gets or sets the ranks in this skill.
        /// </summary>
        public int Ranks
        {
            get { return mRanks; }
            set { CheckPropertyChanged<int>("Ranks", ref mRanks, ref value); }
        }

        /// <summary>
        /// Gets or sets if this skill is a class skill.
        /// </summary>
        public bool IsClassSkill
        {
            get { return mIsClassSkill; }
            set { CheckPropertyChanged<bool>("IsClassSkill", ref mIsClassSkill, ref value); }
        }

        /// <summary>
        /// Gets or sets the miscellanous bonus in a skill.
        /// </summary>
        public int MiscBonus
        {
            get { return mMiscBonus; }
            set { CheckPropertyChanged<int>("MiscBonus", ref mMiscBonus, ref value); }
        }

        /// <summary>
        /// Gets the base StatType for this skill.
        /// </summary>
        public StatType BaseStatType
        {
            get { return mBaseStatType; }
            private set { CheckPropertyChanged<StatType>("BaseStatType", ref mBaseStatType, ref value); }
        }

        /// <summary>
        /// Gets or sets the alternate stat type for this skill.
        /// </summary>
        public StatType? AlternateStatType
        {
            get { return mAlternateStatType; }
            set { CheckPropertyChanged<StatType?>("AlternateStatType", ref mAlternateStatType, ref value); }
        }

        public bool Equals(Skill skill)
        {
            if (skill.Name.Equals(Name, StringComparison.OrdinalIgnoreCase))
            {
                if (string.IsNullOrEmpty(skill.Subtype) && string.IsNullOrEmpty(skill.Subtype))
                {
                    return true;
                }
                else if (!string.IsNullOrEmpty(skill.Subtype) && !string.IsNullOrEmpty(Subtype) && skill.Subtype.Equals(Subtype, StringComparison.OrdinalIgnoreCase))
                {
                    return true;
                }
            }
            return false;
        }

        private static Dictionary<string, StatType> sBaseSkills;
        private static Dictionary<string, List<string>> sSubskills;

        private static object sLockObject = 0.987;

        private static void CreateBaseSkills()
        {
            if (sBaseSkills != null)
            {
                return;
            }

            sBaseSkills = new Dictionary<string, StatType>();
            sSubskills = new Dictionary<string, List<string>>();

            string craftSubtypeNames = "alchemy,armor,baskets,books,bows,calligraphy,carpentry,cloth,clothing,glass,jewelry,leather,locks,paintings,pottery,sculptures,ships,shoes,stonemasonry,traps,weapons";
            string knowledgeSubtypeNames = "Arcana,Dungeoneering,Engineering,Geography,History,Local,Nature,Nobility,Planes,Religion";
            string performSubtypeNames = "Act,Comedy,Dance,Keyboard instruments,Oratory,Percussion instruments,String instruments,Wind instruments,Sing";
            string professionSubtypeNames = "architect,baker,barrister,brewer,butcher,clerk,cook,courtesan,driver,engineer,farmer,fisherman,gambler,gardener,herbalist,innkeeper,librarian,merchant,midwife,miller,miner,porter,sailor,scribe,shepherd,stable master,soldier,tanner,trapper,woodcutter";

            List<string> craftSubtypes = new List<string>(craftSubtypeNames.Split(','));
            List<string> knowledgeSubtypes = new List<string>(knowledgeSubtypeNames.Split(','));
            List<string> performSubtypes = new List<string>(performSubtypeNames.Split(','));
            List<string> professionSubtypes = new List<string>(professionSubtypeNames.Split(','));
            string craft = "Craft";
            string knowledge = "Knowledge";
            string perform = "Perform";
            string profession = "Profession";

            sBaseSkills.Add("Acrobatics", StatType.Dexterity);
            sBaseSkills.Add("Appraise", StatType.Intelligence);
            sBaseSkills.Add("Bluff", StatType.Charisma);
            sBaseSkills.Add("Climb", StatType.Strength);
            sBaseSkills.Add("Diplomacy", StatType.Charisma);
            sBaseSkills.Add("Disable Device", StatType.Dexterity);
            sBaseSkills.Add("Disguise", StatType.Charisma);
            sBaseSkills.Add("Escape Artist", StatType.Dexterity);
            sBaseSkills.Add("Fly", StatType.Dexterity);
            sBaseSkills.Add("Handle Animal", StatType.Charisma);
            sBaseSkills.Add("Heal", StatType.Wisdom);
            sBaseSkills.Add("Intimidate", StatType.Charisma);
            sBaseSkills.Add("Linguistics", StatType.Intelligence);
            sBaseSkills.Add("Perception", StatType.Wisdom);
            sBaseSkills.Add("Ride", StatType.Dexterity);
            sBaseSkills.Add("Sense Motive", StatType.Wisdom);
            sBaseSkills.Add("Sleight of Hand", StatType.Dexterity);
            sBaseSkills.Add("Spellcraft", StatType.Intelligence);
            sBaseSkills.Add("Stealth", StatType.Dexterity);
            sBaseSkills.Add("Survival", StatType.Wisdom);
            sBaseSkills.Add("Swim", StatType.Strength);
            sBaseSkills.Add("Use Magic Device", StatType.Charisma);

            sBaseSkills.Add(craft, StatType.Intelligence);
            sBaseSkills.Add(knowledge, StatType.Intelligence);
            sBaseSkills.Add(perform, StatType.Charisma);
            sBaseSkills.Add(profession, StatType.Wisdom);

            sSubskills.Add(craft, craftSubtypes);
            sSubskills.Add(knowledge, knowledgeSubtypes);
            sSubskills.Add(perform, performSubtypes);
            sSubskills.Add(profession, professionSubtypes);
        }

        private string mName = "";
        private int mRanks = 0;
        private bool mIsClassSkill = false;
        private int mMiscBonus = 0;
        private StatType mBaseStatType;
        private StatType? mAlternateStatType;
        private string mSubtype = null;
    }
}
